#include <asm/asm_macros_common.S>
#include <asm/uart.h>

    .global xmodem_receive


/*void memcpy_S(uint8_t *dst, uint8_t *src, int count) */
func memcpy_S
    li t0, 1

memcpy_L1:
    bltu a2, t0, memcpy_ret
    lbu t1, 0(a1)
    sb t1, 0(a0)
    addi a0, a0, 1
    addi a1, a1, 1
    addi a2, a2, -1
    j memcpy_L1

memcpy_ret:
    ret
endfunc memcpy_S

    /* -----------------------------------------------
	 * int xmodem_receive(u8 *pbuf, u32 size)
	 * Function to receive file by xmodem protol.
     * print debug information.
	 * In: a0 - memory address 
	 *     a1 - memory size
	 * Out: return 0 on success
	 * -----------------------------------------------
	 */
func xmodem_receive
    addi sp, sp, -1152
    sd ra, 1144(sp)
    sd s0, 1136(sp)
    sd s1, 1128(sp)
    sd s2, 1120(sp)
    sd s3, 1112(sp)
    sd s4, 1104(sp)
    sd s5, 1096(sp)
    sd s6, 1088(sp)
    sd s7, 1080(sp)
    sd s8, 1072(sp)
    sd s9, 1064(sp)
    sd s10, 1056(sp)
    sd s11, 1048(sp)
    
    /* unsigned char buf[1024] --> sp + 8 , end at sp + 1032*/
    /* unsigned char *ch = sp + 1040 */
    mv s0, a0  /* receive data buf address */
    mv s1, a1  /* receive data buf size */
    li s2, NACK   /* send NAK flag */
    li s4, 0   /* packet len */
    /* s5 --> packet no*/
    /* s6 --> ~packet no */
    li s7, 1   /* s7 packet index */
    li s8, 8  /* max packet index error */
    li s9, 0  /* pbuf used size */
    li s10, 0  /* checksum */
    /* s3 --> retry count */
    
xmodem_L1:
    li s3, 300

xmodem_L2:
    beqz s2, receive_header
    li a0, NACK
    call uart_putc

receive_header:
    addi a0, sp, 1040  /* &ch */
    li a1, UART_XMODEM_TIMEOUT       /*uart_get timeout */
    call uart_getc   /* return 0, get none charter */
    beqz a0, xmodem_L3

    lbu t0, 1040(sp)   /* t0 = *ch */
    li t1, SOH      /* 128 Bytes*/
    beq t0, t1, start_recv_128B
    li t1, STX
    beq t0, t1, start_recv_1024B
    li t1, EOT
    beq t0, t1, end_recv
    li t1, CAN
    beq t0, t1, host_cancel

xmodem_L3:
    addi s3, s3, -1
    bnez s3, xmodem_L2

    call uart_flush_data    
    li a0, 1 /* Max try count */
    j xmodem_recv_ret

start_recv_1024B:
    li s4, 1024 /* packet len */
    j start_recv
start_recv_128B:
    li s4, 128    /* packet len */
start_recv:
    li s2, 0     /* not send NAK flag*/
    
    addi a0, sp, 1040   /* &ch */
    li a1, UART_XMODEM_TIMEOUT
    call uart_getc
    beqz a0, packet_reject    /* uart get data fail*/
    lbu s5, 1040(sp)   /* packet no*/

    addi a0, sp, 1040 /* &ch */
    li a1, UART_XMODEM_TIMEOUT
    call uart_getc
    beqz a0, packet_reject    /* uart get data fail*/
    lbu s6, 1040(sp)   /* ~packet no */

    sw s4, 4(sp) /* store packet size*/
    li s10, 0     /* packet checksum */ 
    addi s11, sp, 8  /* buf[1024] , size 1024, end at sp + 1032*/

recv_data:
    addi a0, sp, 1040
    li a1, UART_XMODEM_TIMEOUT
    call uart_getc
    beqz a0, packet_reject    /* uart get data fail*/
    lbu a0, 1040(sp)   /* uart data */
    sb a0, 0(s11)    /* t4 -->buf at stack --> buf[1024] */
    add s10, s10, a0  /*calcaulte check sum */
    andi s10, s10, 0xff
    addi s11, s11, 1
    addi s4, s4, -1   
    bnez s4, recv_data /* loop count */

    lw s4, 4(sp) /* load packe size */
recv_check_sum:
    addi a0, sp, 1040  /* &ch */
    li a1, UART_XMODEM_TIMEOUT
    call uart_getc
    beqz a0, packet_reject    /* uart get data fail*/
    lbu a0, 1040(sp)
    bne a0, s10,  packet_reject         /*check sum not equal*/ 

    xori t1, s6, 0xff        /* s6 --> ~packet no, t1=~s6 */
    bne s5, t1, packet_reject /*s5 --> packet no*/
    
    bne s5, s7, packet_index_err  /* s7 --> packet index*/
    addi s7, s7, 1  /* packet index + 1 */
    andi s7, s7, 0xff
    li s8, 8  /* updata max packet index error count */

    add t1, s9, s4  /* update pbuf size */
    bltu s1, t1, buf_size_err  /*size exceed pbuf size*/
    add a0, s9, s0 /*dest addr = s0 + s9*/
    addi a1, sp, 8  /* buf[1024], end at sp + 1032*/
    mv a2, s4 
    call memcpy_S  /*copy data from stack (buf[1024]) to memory*/
    add s9, s9, s4
    j packet_index_ok

packet_index_err:
    addi t0, s7, -1
    bne s5, t0, packet_reject
    addi s8, s8, -1   /* s8 --> max packet index error count */
    beqz s8, cancel_recv  

packet_index_ok:
    li a0, ACK
    call uart_putc
    j xmodem_L1

packet_reject:
    call uart_flush_data
    li a0, NACK
    call uart_putc
    j xmodem_L1

end_recv:
    call uart_flush_data
    li a0, ACK
    call uart_putc
    li a0, 0  
    j xmodem_recv_ret /* return 0 --> success */

host_cancel:
    addi a0, sp, 1040
    li a1, UART_XMODEM_TIMEOUT
    call uart_getc
    beqz a0, cancel_recv
    lbu t0, 1040(sp)
    li t1, CAN
    bne t0, t1, cancel_recv

    call uart_flush_data
    li a0, ACK
    call uart_putc
    li a0, 2  /* return 2 --> host cancel transfer */
    j xmodem_recv_ret

cancel_recv:
    call uart_flush_data
    li a0, CAN
    call uart_putc
    li a0, CAN
    call uart_putc
    li a0, CAN
    call uart_putc
    li a0, 4  /* return 4 --> cancel recveive */
    j xmodem_recv_ret

buf_size_err:
    call uart_flush_data
    li a0, CAN
    call uart_putc
    li a0, 5  /* return 5 --> buf size error */

xmodem_recv_ret:
    ld s11, 1048(sp)
    ld s10, 1056(sp)
    ld s9, 1064(sp)
    ld s8, 1072(sp)
    ld s7, 1080(sp)
    ld s6, 1088(sp)
    ld s5, 1096(sp)
    ld s4, 1104(sp)
    ld s3, 1112(sp)
    ld s2, 1120(sp)
    ld s1, 1128(sp)
    ld s0, 1136(sp)
    ld ra, 1144(sp)
    addi sp, sp, 1152
    ret
endfunc xmodem_receive
